/*    
 * Copyright (c) 2013 ISoftStone, Inc.
 * All right reserved.
 *
 * 文件名：      ChargingServiceImpl.java
 * 作者:     Jacky Wang
 * 创建日期： 2013-7-23 下午11:30:56
 * 版本：           
 *
 */
package com.isoftstone.industry.aaa.core.service.accounting.impl;

import java.io.File;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.isoftstone.industry.constant.UnitConstant;
import com.isoftstone.industry.persistance.bean.BillingCycle;
import com.isoftstone.industry.persistance.bean.ChargingItem;
import com.isoftstone.industry.persistance.bean.ChargingItemDetail;
import com.isoftstone.industry.persistance.bean.PricingInfo;
import com.isoftstone.industry.persistance.bean.PricingInfoSnapshot;
import com.isoftstone.industry.persistance.bean.ProductInstance;
import com.isoftstone.industry.persistance.bean.TariffSnapshot;
import com.isoftstone.industry.persistance.bean.Unit;
import com.isoftstone.industry.persistance.bean.User;
import com.isoftstone.industry.aaa.core.log.BusinessLogger;
import com.isoftstone.industry.aaa.core.log.ILogMessages;
import com.isoftstone.industry.aaa.core.service.accounting.IChargingService;
import com.isoftstone.industry.aaa.core.service.accounting.charging.IChargingEngine;
import com.isoftstone.industry.aaa.core.service.accounting.charging.impl.ChargingEnginerBuilder;
import com.isoftstone.industry.aaa.core.service.assist.IChargingItemService;
import com.isoftstone.industry.aaa.core.service.assist.IUserService;
import com.isoftstone.industry.aaa.utils.SystemProperties;
import com.isoftstone.modules.core.utils.DateUtils;
import com.isoftstone.modules.core.utils.lock.FileProgrameLock;
import com.isoftstone.modules.core.utils.lock.Lock;

/**
 * @author jacky
 */
@Service
public class ChargingServiceImpl implements IChargingService {
	@Resource
	private BusinessLogger logger;

	@Resource
	private IChargingItemService chargingItemService;

	@Resource
	private IUserService userService;

	@Resource
	private SystemProperties systemProperties;

	/**
	 * 在出帐前为用户计算后付费周期性费用
	 * 
	 * @param
	 * @return
	 * @throws
	 */
	private void charge4User(User user, BillingCycle billingCycle) {
		logger.info(ILogMessages.CHARGING_START4USER, user.getUsername(),
				billingCycle.getName());
		// 找出该用户需要进行计费的计费子项
		List<ChargingItem> chargingItemList = chargingItemService
				.findNeedChargeItemsByUserAndBillingCycle(user.getId(),
						billingCycle);
		if (chargingItemList.size() > 0) {
			for (ChargingItem chargingItem : chargingItemList) {
				if (isNeedCharging4Item(chargingItem)) {
					ProductInstance prodInst = chargingItem.getProdInst();
					logger.info(ILogMessages.CHARGING_ITEM_START4USER, user
							.getUsername(), prodInst.getSn(), billingCycle
							.getName());
					// 获取具体的计费信息
					Set<ChargingItemDetail> details = chargingItem.getDetails();
					for (ChargingItemDetail detail : details) {
						if (isNeedCharging4Detail(detail, billingCycle)) {
							PricingInfoSnapshot pricingInfoSnap = detail
									.getPricingInfoSnapshot();
							String tariffType = pricingInfoSnap.getTariffType();
							IChargingEngine chargingEnginer = ChargingEnginerBuilder
									.build(tariffType, user, billingCycle,
											detail);
							chargingEnginer.charge4Postpaid();
						}
					}

					// 更新计费子项的上次计费时间
					chargingItemService.changeLastChargingTime(chargingItem
							.getId(), DateUtils.getCurrentTimestamp());
				}
			}
		} else {
			logger.info(ILogMessages.CHARGING_NOCHARGE4USER,
					user.getUsername(), billingCycle.getName());
		}
	}

	/**
	 * 出帐前计费：判断该计费子项明细是否需要进行计费,满足以下条件:</br>
	 * <ol>
	 * <li>计费子项明细有效</li>
	 * <li>后付费+周期性：判断上次计费时间与当前时间的间隔是否已经达到周期间隔</li>
	 * <li>后付费+一次性/使用量：需要进行计费</li>
	 * <li>预付费：不需要计费</li>
	 * </ol>
	 * 
	 * @param
	 * @return
	 * @throws
	 */
	private boolean isNeedCharging4Detail(ChargingItemDetail detail,
			BillingCycle billingCycle) {
		boolean result = false;
		if (ChargingItemDetail.Status.VALID.equals(detail.getStatus())) {
			PricingInfoSnapshot _pricingInfo = detail.getPricingInfoSnapshot();
			String chargeMode = _pricingInfo.getChargeMode();
			String tariffType = _pricingInfo.getTariffType();
			// 后付费
			if (PricingInfo.ChargeMode.POST_PAY.equals(chargeMode)) {
				// 周期性
				if (PricingInfo.ITariffTypes.PERIODIC.equals(tariffType)) {
					Date lastEnd = detail.getChargingEndDate();
					Timestamp now = DateUtils.getCurrentTimestamp();
					TariffSnapshot tariffSnap = _pricingInfo.getTariffList()
							.get(0);
					Unit timingUnit = tariffSnap.getTimingUnit(); // 间隔时间单位
					int interval = tariffSnap.getTimingRate().intValue();
					// 还没有计费过
					if (lastEnd == null) {
						result = true;
					} else {
						// 日
						if (UnitConstant.TIME.DAY.equals(timingUnit.getCode())) {
							int interval_d = DateUtils.getIntervalDays(lastEnd,
									now);
							result = interval_d >= interval;
							// 月
						} else if (UnitConstant.TIME.MONTH.equals(timingUnit
								.getCode())) {
							int interval_m = DateUtils.getIntervalMonths(
									lastEnd, now);
							result = interval_m >= interval;
							// 年
						} else if (UnitConstant.TIME.YEAR.equals(timingUnit
								.getCode())) {
							int interval_y = DateUtils.getIntervalYears(
									lastEnd, now);
							result = interval_y >= interval;
						}
					}
				} else {
					result = true;
				}
			} else {
				result = false;
			}
		} else {
			result = false;
		}
		return result;
	}

	/**
	 * 定时计费：判断该计费子项明细是否需要进行计费,满足以下条件:</br>
	 * <ol>
	 * <li>计费子项明细有效</li>
	 * <li>预付费+周期性：判断上次计费时间与当前时间的间隔是否已经达到周期间隔</li>
	 * <li>预付费+一次性/使用量：需要进行计费</li>
	 * <li>后付费：不需要计费</li>
	 * </ol>
	 * 
	 * @param
	 * @return
	 * @throws
	 */
	private boolean isNeedTimingCharging4Detail(ChargingItemDetail detail) {
		boolean result = false;
		if (ChargingItemDetail.Status.VALID.equals(detail.getStatus())) {
			PricingInfoSnapshot _pricingInfo = detail.getPricingInfoSnapshot();
			String chargeMode = _pricingInfo.getChargeMode();
			String tariffType = _pricingInfo.getTariffType();
			Timestamp lastChargeTime = detail.getLastChargingTime();
			Timestamp now = DateUtils.getCurrentTimestamp();
			// 预付费
			if (PricingInfo.ChargeMode.PRE_PAY.equals(chargeMode)) {
				// 周期性
				if (PricingInfo.ITariffTypes.PERIODIC.equals(tariffType)) {
					TariffSnapshot tariffSnap = _pricingInfo.getTariffList()
							.get(0);
					Unit timingUnit = tariffSnap.getTimingUnit(); // 间隔时间单位
					int interval = tariffSnap.getTimingRate().intValue();
					// 还没有计费过
					if (lastChargeTime == null) {
						result = true;
					} else {
						// 日
						if (UnitConstant.TIME.DAY.equals(timingUnit.getCode())) {
							int interval_d = DateUtils.getIntervalDays(
									lastChargeTime, now);
							result = interval_d >= interval;
							// 月
						} else if (UnitConstant.TIME.MONTH.equals(timingUnit
								.getCode())) {
							int interval_m = DateUtils.getIntervalMonths(
									lastChargeTime, now);
							result = interval_m >= interval;
							// 年
						} else if (UnitConstant.TIME.YEAR.equals(timingUnit
								.getCode())) {
							int interval_y = DateUtils.getIntervalYears(
									lastChargeTime, now);
							result = interval_y >= interval;
						}
					}
				} else {
					result = true;
				}
			} else {
				result = false;
			}
		} else {
			result = false;
		}
		return result;
	}

	/**
	 * 判断该计费子项是否需要进行计费
	 * 
	 * @param
	 * @return
	 * @throws
	 */
	private boolean isNeedCharging4Item(ChargingItem chargingItem) {
		// 比较当前日期与计费子项的失效日期，如果当前日期大于失效日期，则标识为失效状态
		Date expDate = chargingItem.getExpDate();
		expDate = DateUtils.createEndTime(expDate);
		Date today = DateUtils.today();
		if (expDate != null && today.after(expDate)) {
			chargingItem.setStatus(ChargingItem.Status.INVALID);
			chargingItemService.markInvalid(chargingItem.getId());
		}
		return ChargingItem.Status.VALID.equals(chargingItem.getStatus());
	}

	@Override
	public void charge4UserInBillingCycle(User user, BillingCycle billingCycle) {
		charge4User(user, billingCycle);
	}

	@Override
	@Transactional
	public void timingCharging() {
		List<User> userList = userService.findNeedChargingUsers();
		for (User user : userList) {
			Lock lock = getFileLock(user);
			// 若可以成功获取文件锁，则代表没有进程正在为该用户进行计费
			if (lock.obtain() == true) {
				try {
					charge4User(user.getId());
				}catch (Exception e){
					logger.error(e.getMessage(), e);
				} finally {
					// 释放文件锁
					lock.unlock();
				}
			}
		}

	}

	private Lock getFileLock(User user) {
		String fileName = systemProperties.getFileLockDir() + File.separator
				+ "charging_" + user.getId() + ".lock";
		return FileProgrameLock.get(fileName);
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void charge4User(String userId) {
		User user = userService.findOne(userId);
		logger.info(ILogMessages.CHARGING_START4USER, user.getUsername());
		// 找出该用户需要进行计费的计费子项
		Timestamp now = DateUtils.getCurrentTimestamp();
		List<ChargingItem> chargingItemList = chargingItemService
				.findNeedChargeItemsByUser(user.getId(), now);
		if (chargingItemList.size() > 0) {
			for (ChargingItem chargingItem : chargingItemList) {
				if (isNeedCharging4Item(chargingItem)) {
					ProductInstance prodInst = chargingItem.getProdInst();
					logger.info(ILogMessages.CHARGING_ITEM_START4USER, user
							.getUsername(), prodInst.getSn());
					// 获取具体的计费信息
					Set<ChargingItemDetail> details = chargingItem.getDetails();
					for (ChargingItemDetail detail : details) {
						if (isNeedTimingCharging4Detail(detail)) {
							PricingInfoSnapshot pricingInfoSnap = detail
									.getPricingInfoSnapshot();
							String tariffType = pricingInfoSnap.getTariffType();
							IChargingEngine chargingEnginer = ChargingEnginerBuilder
									.build(tariffType, user, null, detail);
							chargingEnginer.charge4Prepaid();
						}
					}

					// 更新计费子项的上次计费时间
					chargingItemService.changeLastChargingTime(chargingItem
							.getId(), DateUtils.getCurrentTimestamp());
				}
			}
		} else {
			logger
					.info(ILogMessages.CHARGING_NOCHARGE4USER, user
							.getUsername());
		}
	}

}
